package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.jpush.api.report.UsersResult;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.oms.service.IOmsInventoryService;
import com.arpa.wms.cache.*;
import com.arpa.wms.common.util.OrderIdGeneratorUtil;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.dto.DeliveryExcelDTO;
import com.arpa.wms.domain.dto.InventoryDTO;
import com.arpa.wms.domain.dto.InventoryOperationDto;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.InventoryJobStatusEnum;
import com.arpa.wms.domain.enums.InventoryRecordTypeEnum;
import com.arpa.wms.domain.enums.StockoutTypeEnum;
import com.arpa.wms.domain.enums.TempLocationTypeEnum;
import com.arpa.wms.domain.vo.*;
import com.arpa.wms.mapper.GoodsUnitConversionMapper;
import com.arpa.wms.mapper.InventoryMapper;
import com.arpa.wms.mapper.LocationMapper;
import com.arpa.wms.mapper.WarehouseSpaceMapper;
import com.arpa.wms.service.IBatchRuleService;
import com.arpa.wms.service.IInventoryRecordService;
import com.arpa.wms.service.IInventoryService;
import com.arpa.wms.service.InventoryOperationService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.testng.collections.Lists;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.rmi.ServerException;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 库存 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-08-04
 */
@Service
@Log4j2
public class InventoryServiceImpl extends ServiceImpl<InventoryMapper, Inventory> implements IInventoryService {


    private final WarehouseCache warehouseCache;

    private final WarehouseSpaceCache warehouseSpaceCache;

    private final LocationCache locationCache;

    private final IOmsInventoryService iOmsInventoryService;

    @Resource
    private   LocationMapper locationMapper;

    private final PartyCache partyCache;

    private final UnitCache unitCache;

    private final GoodsCache goodsCache;

    private final BatchRuleCache batchRuleCache;

    private final IBatchRuleService iBatchRuleService;

    private final IInventoryRecordService iInventoryRecordService;
    private final WarehouseShipmentCache warehouseShipmentCache;

    private final InventoryOperationService inventoryOperationService;

    @Resource
    private  WarehouseSpaceMapper warehouseSpaceMapper;

    @Resource
    private GoodsUnitConversionMapper goodsUnitConversionMapper;

    @Autowired
    private GoodsUnitConversionCache goodsUnitConversionCache;

    public InventoryServiceImpl(WarehouseCache warehouseCache,
                                WarehouseSpaceCache warehouseSpaceCache,
                                LocationCache locationCache,
                                IOmsInventoryService iOmsInventoryService, PartyCache partyCache,
                                UnitCache unitCache,
                                GoodsCache goodsCache,
                                BatchRuleCache batchRuleCache,
                                IBatchRuleService iBatchRuleService,
                                IInventoryRecordService iInventoryRecordService,
                                WarehouseShipmentCache warehouseShipmentCache,
                                InventoryOperationService inventoryOperationService) {
        this.warehouseCache = warehouseCache;
        this.warehouseSpaceCache = warehouseSpaceCache;
        this.locationCache = locationCache;
        this.iOmsInventoryService = iOmsInventoryService;
        this.partyCache = partyCache;
        this.unitCache = unitCache;
        this.goodsCache = goodsCache;
        this.batchRuleCache = batchRuleCache;
        this.iBatchRuleService = iBatchRuleService;
        this.iInventoryRecordService = iInventoryRecordService;
        this.warehouseShipmentCache = warehouseShipmentCache;
        this.inventoryOperationService = inventoryOperationService;
    }


    /**
     * 保存
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(Inventory entity) {
        if (StringUtils.isBlank(entity.getCode())) {
            entity.setCode(IdUtil.simpleUUID());
        }
        if (StringUtils.isBlank(entity.getCreatedBy())) {
            entity.setCreatedBy(UserUtil.getCode());
            entity.setCreatedName(partyCache.translate(entity.getCreatedBy()));
        }
        return super.save(entity);
    }

    /**
     * 根据code更新实体
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(Inventory entity) {
        if (StringUtils.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        return baseMapper.update(entity, new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, entity.getCode()).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
    }

    /**
     * 查询列表
     *
     * @param inventoryDTO
     * @return
     */
    @Override
    public List<InventoryVO> queryList(InventoryDTO inventoryDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(inventoryDTO.getSortField())) {
            inventoryDTO.setSortField(CommonUtil.camel2Underline(inventoryDTO.getSortField()));
        }
        List<InventoryVO> inventoryVOList = baseMapper.queryList(inventoryDTO);

        // 翻译
        //inventoryVOList.forEach(e -> {
        //    e.setWarehouseName(warehouseCache.translate(e.getWarehouseCode()));
        //
        //    if (StrUtil.isNotBlank(e.getWarehouseSpaceCode())) {
        //        e.setWarehouseSpaceName(warehouseSpaceCache.translate(e.getWarehouseSpaceCode()));
        //    }
        //    if (StrUtil.isNotBlank(e.getLocation())) {
        //        e.setLocationName(locationCache.translate(e.getLocation()));
        //    }
        //    e.setUnitName(unitCache.translate(e.getGoodsUnit()));
        //    if (StrUtil.isNotBlank(e.getCreatedBy())) {
        //        e.setCreatedByName(partyCache.translate(e.getCreatedBy()));
        //    }
        //    if (StrUtil.isNotBlank(e.getModifiedBy())) {
        //        e.setModifiedByName(partyCache.translate(e.getModifiedBy()));
        //    }
        //    e.setProductCode(e.getGoodCode());
        //});
        // 添加批次规则
        if (IterUtil.isNotEmpty(inventoryVOList)) {
            inventoryVOList.stream().forEach(item -> {
                Map<String, Object> goodsCacheMap = goodsCache.get(item.getGoodsCode());
                if(MapUtil.isNotEmpty(goodsCacheMap)){
                    item.setBatchRule((Map<String, Object>) goodsCacheMap.get("batchRule"));
                }
                //Map<String, Object> batchRuleMap = batchRuleCache.get((String) goodsCacheMap.get(LOT_NUMBER));
                //item.setBatchRule(batchRuleMap);
            });
        }

        return inventoryVOList;
    }

    /**
     * 查询汇总数量
     * @param inventoryDTO
     * @return
     */
    @Override
    public InventoryVO querySumQuantity(InventoryDTO inventoryDTO){
        return baseMapper.querySumQuantity(inventoryDTO);
    }

    /**
     * 库存 导出数据
     * @param inventoryDTO
     * @return
     */
    @Override
    public List<InventoryImportVO> importInventory(InventoryDTO inventoryDTO) {
        List<InventoryImportVO> inventoryVOList =  baseMapper.importInventory(inventoryDTO);
        //赋值
        inventoryVOList.forEach(e->{
            if(StrUtil.isNotEmpty(e.getWarehouseCode())){
                e.setWarehouseName(warehouseCache.translate(e.getWarehouseCode()));
            }
            if(StrUtil.isNotEmpty(e.getWarehouseSpaceCode())){
                e.setWarehouseSpaceName(warehouseSpaceCache.translate(e.getWarehouseSpaceCode()));
            }
            if(StrUtil.isNotEmpty(e.getGoodsCode())){
                List<GoodsUnitConversion> goodsUnitConversions = goodsUnitConversionCache.get(e.getGoodsCode());
                if (IterUtil.isEmpty(goodsUnitConversions)) {
                    log.error("商品code【" + e.getGoodsCode() +"】没有单位缓存信息");
                }
                GoodsUnitConversion goodsUnitConversion = goodsUnitConversions.get(0);
                e.setLength(goodsUnitConversion.getLength());
                e.setWidth(goodsUnitConversion.getWidth());
                e.setHeight(goodsUnitConversion.getHeight());
                e.setWeight(goodsUnitConversion.getWeight());
                e.setVolume(goodsUnitConversion.getVolume());
            }
            e.setAvailableQuantity(e.getQuantity().subtract(e.getUseQuantity()));
            if(ObjectUtil.isNotNull(e.getWeight())  && ObjectUtil.isNotNull(e.getAvailableQuantity())){  //可用数量 * 重量
                e.setWeight(e.getAvailableQuantity().multiply(e.getWeight()));
            }
            if(ObjectUtil.isNotNull(e.getVolume())  && ObjectUtil.isNotNull(e.getAvailableQuantity())){  //可用数量 * 体积
                e.setWeight(e.getAvailableQuantity().multiply(e.getVolume()));
            }
        });
        return inventoryVOList;
    }

    /**
     * 查询导入单合计,包含总数
     * @param inventoryDTO
     * @return
     */
    @Override
    public InventoryImportVO importInventorySum(InventoryDTO inventoryDTO){
        return this.baseMapper.importInventorySum(inventoryDTO);
    }


    public static String format(String value, Object... paras) {
        return MessageFormat.format(value, paras);
    }


    @Override
    public List<InventoryVO> queryByCondition(InventoryDTO inventoryDTO) {
        return baseMapper.queryByCondition(inventoryDTO);
    }

    @Override
    public List<InventoryVO> queryByInventoryStatus(List<String> statusList) {
        return baseMapper.queryByInventoryStatus(statusList,UserUtil.getBranchCode());
    }

    /**
     * 查询合计,包含总数
     *
     * @param inventoryDTO
     * @return
     */
    @Override
    public InventoryVO queryListSum(InventoryDTO inventoryDTO) {
        InventoryVO inventoryVO = baseMapper.queryListSum(inventoryDTO);
        return inventoryVO;
    }

    /**
     * 锁库存
     *
     * @param code
     */
    @Override
    public void lock(String code) {
        this.update(new UpdateWrapper<Inventory>().lambda().eq(Inventory::getCode, code).eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .set(Inventory::getIsLock, "1"));
    }

    /**
     * 解锁库存
     *
     * @param code
     */
    @Override
    public void unlock(String code) {
        this.update(new UpdateWrapper<Inventory>().lambda().eq(Inventory::getCode, code).eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .set(Inventory::getIsLock, "0"));
    }

    /**
     * 批次调整
     *
     * @param adjustInventory
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result lotAdjust(InventoryDTO adjustInventory) {
        // 调整数量
        BigDecimal adjustQuantity = adjustInventory.getAdjustQuantity();

        if (adjustQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("调整数量不能小于0");
        }
        // 判断调整数量是否超过可调整数量（从数据库中重新获取，判断是否超过 库存数量-锁定数量）
        Inventory oldInventory = this.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, adjustInventory.getCode()).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
        BigDecimal canAdjustQuantity = oldInventory.getQuantity().subtract(oldInventory.getUseQuantity());
        if (canAdjustQuantity.compareTo(adjustQuantity) < NumConst.NUM_ZERO) {
            return Result.error("调整数量超过可用数量，请确认");
        }

        // 库存状态是否发生变化，如果发生变化，则在最后需要调整oms不可用库存数量
        boolean changeGoodsStatusFlag = true;
        if (oldInventory.getGoodsStatus().equals(adjustInventory.getGoodsStatus())) {
            changeGoodsStatusFlag = false;
        }

        // 根据老库存、调整库存信息生成新的库存信息
        Inventory tempNewInv = new Inventory();
        BeanUtil.copyProperties(oldInventory, tempNewInv);
        BeanUtil.copyProperties(adjustInventory, tempNewInv);
        tempNewInv.setQuantity(adjustInventory.getAdjustQuantity());
        tempNewInv.setUseQuantity(BigDecimal.ZERO);
        tempNewInv.setCode(IdUtil.simpleUUID());
        // 可能存在批次规则调整的情况，所以需要重新根据批次规则，对已有数据，但规则中没有勾选的信息进行清理
        adjustInventoryValueByBatchRule(tempNewInv);
        tempNewInv.setLot(OrderIdGeneratorUtil.generatorLot(tempNewInv));
        tempNewInv.setFullLot(OrderIdGeneratorUtil.generatorFullLot(tempNewInv));

        // 老库存减少
        boolean adjustFlag = this.adjustQuantity(oldInventory.getCode(), adjustQuantity.negate());
        if (!adjustFlag) {
            throw new ServiceException("调整原库存数量失败");
        }
        // 新库存增加
        // 判断是否有相同库存
        Inventory newInv = this.hasSameInventory(tempNewInv.getLot(), tempNewInv.getGoodsCode(), tempNewInv.getLocation(),
                tempNewInv.getShipmentCode(), tempNewInv.getGoodsStatus(), tempNewInv.getIsLock(), tempNewInv.getContainerBarCode()
                ,tempNewInv.getGmtStock());

        // 有相同批次则调整该库存的数量，没有，则新生成一条库存信息
        if (newInv != null) {
            this.adjustQuantity(newInv.getCode(), adjustQuantity);
        } else {
            this.save(tempNewInv);
            newInv = tempNewInv;
        }

        // 记录库存变动日志
        updateInventoryRecord(oldInventory, newInv, adjustInventory.getAdjustQuantity());



        return Result.ok();
    }


    /**
     * 记录库存变动日志
     *
     * @param oldInventory
     * @param newInventory
     * @param changeQuantity
     */
    public void updateInventoryRecord(Inventory oldInventory, Inventory newInventory, BigDecimal changeQuantity) {
        // 库存调整需要记录两条，原库存减少，新库存增加，同时由于没有来源单号，来源单号为空
        iInventoryRecordService.insertRecord(oldInventory, "", changeQuantity.negate(), InventoryRecordTypeEnum.ADJUST.getValue());
        iInventoryRecordService.insertRecord(newInventory, "", changeQuantity, InventoryRecordTypeEnum.ADJUST.getValue());
    }


    /**
     * 查询可移库库存
     *
     * @param inventoryDTO
     * @return
     */
    @Override
    public List<InventoryVO> queryCanMoveList(InventoryDTO inventoryDTO) {
        return this.baseMapper.queryCanMoveList(inventoryDTO);
        // 翻译
        //inventoryVOS.forEach(e -> {
            //e.setWarehouseName(warehouseCache.translate(e.getWarehouseCode()));
            //e.setWarehouseSpaceName(warehouseSpaceCache.translate(e.getWarehouseSpaceCode()));
            //e.setLocationName(locationCache.translate(e.getLocation()));
            //e.setUnitName(unitCache.translate(e.getGoodsUnit()));
            //e.setCreatedByName(partyCache.translate(e.getCreatedBy()));
            //e.setModifiedByName(partyCache.translate(e.getModifiedBy()));
            //e.setProductCode(e.getGoodCode());
        //});
    }

    @Override
    public InventoryVO queryCanMoveListSum(InventoryDTO inventoryDTO) {
        return this.baseMapper.queryCanMoveListSum(inventoryDTO);
    }

    @Override
    public InventoryVO getOneByCode(String code) {
        Inventory entity = this.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, code).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
        InventoryVO inventoryVO = new InventoryVO();
        BeanUtil.copyProperties(entity, inventoryVO);
        inventoryVO.setModifiedByName(partyCache.translate(inventoryVO.getModifiedBy()));
        inventoryVO.setCreatedByName(partyCache.translate(inventoryVO.getCreatedBy()));

        Map<String, Object> goodsMap = goodsCache.get(inventoryVO.getGoodsCode());
        if (goodsMap != null) {
            BatchRule batchRule = iBatchRuleService.getOne(new QueryWrapper<BatchRule>().lambda().eq(BatchRule::getCode, goodsMap.get("lotNumber").toString()).eq(BatchRule::getGroupCode,UserUtil.getBranchCode()));
            if (batchRule != null) {
                BatchRuleVO batchRuleVO = new BatchRuleVO();
                BeanUtil.copyProperties(batchRule, batchRuleVO);
                inventoryVO.setGroupCode(UserUtil.getBranchCode());
                inventoryVO.setBatchRuleVO(batchRuleVO);
            }
        }
        return inventoryVO;
    }

    @Override
    public List<InventoryWithDistributionVO> queryCanDistributeInventory(String warehouseCode, String shipmentCode, String goodsCode) {
        List<InventoryWithDistributionVO> inventoryWithDistributionVOS = this.baseMapper.queryCanDistributeInventory(warehouseCode, shipmentCode, goodsCode,UserUtil.getBranchCode());
        String warehouseName = warehouseCache.translate(warehouseCode);
        // 翻译减少使用缓存，使用sql关联查询，如果使用缓存查询仓库名、库区名、库位名，翻译时间会略长，60条大约4S左右
        inventoryWithDistributionVOS.stream().forEach(e -> {
            e.setWarehouseName(warehouseName);
        });
        return inventoryWithDistributionVOS;
    }

    /**
     * 锁库存
     *
     * @param code
     * @param distributionQuantity
     * @return
     */
    @Override
    public Result lockInventory(String code, BigDecimal distributionQuantity) {
        // XYTODO TODO 下一步需要添加锁，避免操作期间数据变化
        Inventory inventory = this.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, code).eq(Inventory::getDeleted, 0).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
        if (inventory == null) {
            return Result.error("库存不存在，请确认");
        }
        if (inventory.getQuantity().subtract(inventory.getUseQuantity()).compareTo(distributionQuantity) < 0) {
            return Result.error("可用库存小于锁定库存， 请确认");
        }

        InventoryDTO inventoryDTO = new InventoryDTO();
        inventoryDTO.setCode(code);
        inventoryDTO.setUseQuantity(distributionQuantity);
        this.baseMapper.adjustUseQuantity(inventoryDTO);

        return Result.ok();
    }

    /**
     * 查询可以自动分配的库存
     *
     * @param inventoryDTO
     * @return
     */
    @Override
    public List<UsefulInventoryVO> getCanAutoDistributedInventory(InventoryDTO inventoryDTO,Integer isDeliveringIfStockout) {
        inventoryDTO.setGroupCode(UserUtil.getBranchCode());
        // 根据商品code、入驻商、仓库、库存非锁状态、库位非锁状态、可用数量大于0来判断、库存状态可发货  获取所有可用库存
        List<UsefulInventoryVO> usefulInventoryVOS = this.baseMapper.getCanAutoDistributedInventory(inventoryDTO);
        if (NumConst.NUM_ONE.equals(isDeliveringIfStockout)) {
            if(IterUtil.isEmpty(usefulInventoryVOS)){
                return new ArrayList<UsefulInventoryVO>();
            }
        }
        if(IterUtil.isEmpty(usefulInventoryVOS)){
            throw new ServiceException("商品【" + goodsCache.translate(inventoryDTO.getGoodsCode()) + "】没有可用库存");
        }
        // 判断时间排序规则，生产日期、过期日期、存货日期哪种方式排序
        final int dateSortType = getDateSortType(inventoryDTO.getGoodsCode());
        // 根据可用数量（倒序）、时间规则、库位拣货动线号排序
        usefulInventoryVOS = usefulInventoryVOS.stream().sorted(
                Comparator.comparing(UsefulInventoryVO::getCanUseQuantity).reversed()
                        .thenComparing(e -> sortByDate(dateSortType, e), Comparator.nullsLast(Comparator.naturalOrder()))
                        .thenComparing(UsefulInventoryVO::getPickNumber, Comparator.nullsLast(Integer::compareTo))
        ).collect(Collectors.toList());

        return usefulInventoryVOS;
    }

    /**
     * 根据类型判断自动分配库存时的时间排序字段
     * @param type
     * @param vo
     * @return 返回排序字段值
     */
    public String sortByDate(int type, UsefulInventoryVO vo){
        if(type == 0){
            return ObjectUtil.isNotNull(vo.getGmtManufacture())?vo.getGmtManufacture().toString():null;
        }else if(type == 1){
            return ObjectUtil.isNotNull(vo.getGmtExpire())?vo.getGmtExpire().toString():null;
        }else{
            return ObjectUtil.isNotNull(vo.getGmtStock())?vo.getGmtStock().toString():null;
        }
    }

    /**
     * 根据商品code，查询批次规则，判断库存排序规则， 生产日期>过期日期>存货日期，优先生产日期，没有则使用过期日期，还没有则使用存货日期
     * @param goodsCode
     * @return
     */
    public int getDateSortType(String goodsCode){
        Map<String, Object> goodsMap = goodsCache.get(goodsCode);
        Map<String, Object> batchRuleMap = batchRuleCache.get(MapUtil.getStr(goodsMap,"lotNumber"));
        // 时间排序类型，0： 根据生产日期排序；1：过期日期；2：存货日期
        int dateSortType = 2;
        if(StrUtil.equals(MapUtil.getStr(batchRuleMap,"gmtManufacture"), "1")){
            dateSortType = 0;
        }else if(StrUtil.equals(MapUtil.getStr(batchRuleMap,"gmtExpire"), "1")){
            dateSortType = 1;
        }
        return dateSortType;
    }


    /**
     *
     * 判断是否有相同的库存，并返回该库存信息，只查询非临时库位的
     * @param lot
     * @param goodsCode
     * @param location
     * @param shipmentCode
     * @param goodsStatus
     * @param isLock
     * @param containerBarCode
     * @return
     */
    @Override
    public Inventory hasSameInventory(String lot, String goodsCode, String location, String shipmentCode, String goodsStatus,
                                      Integer isLock, String containerBarCode, LocalDateTime gmtStock){
        // 批次、库位、商品、入驻商（ 商品、货主绑定在一起，似乎可以省略）、库存状态、是否锁定、容器（待扩展）,
        LambdaQueryWrapper<Inventory> queryWrapper = new QueryWrapper<Inventory>().lambda();
        queryWrapper.eq(Inventory::getLot, lot)
                .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .eq(Inventory::getLocation, location)
                .eq(Inventory::getGoodsCode, goodsCode)
                .eq(Inventory::getShipmentCode, shipmentCode)
                .eq(Inventory::getGoodsStatus, goodsStatus)
                //.eq(Inventory::getGmtStock, gmtStock)
                .isNull(ObjectUtil.isNull(gmtStock), Inventory::getGmtStock)
                .likeRight(ObjectUtil.isNotNull(gmtStock), Inventory::getGmtStock, ObjectUtil.isNotNull(gmtStock)?gmtStock.toLocalDate():"")
                .eq(Inventory::getIsLock, isLock)
                .eq(Inventory::getTempInventory, NumConst.NUM_ZERO)
                .isNull(ObjectUtil.isNull(containerBarCode), Inventory::getContainerBarCode)
                .eq(ObjectUtil.isNotNull(containerBarCode), Inventory::getContainerBarCode, containerBarCode)
                .gt(Inventory::getQuantity, BigDecimal.ZERO)
                .eq(Inventory::getJobStatus, InventoryJobStatusEnum.NORMAL.getValue());

        Inventory sameInventory = this.getOne(queryWrapper.last("LIMIT 1"));
        return sameInventory;
    }


    /**
     *
     * 判断是否有相同的库存，并返回该库存信息，只查询非临时库位的（用于期初导入）
     * @param lot
     * @param goodsCode
     * @param location
     * @param shipmentCode
     * @param goodsStatus
     * @param isLock
     * @return
     */
    @Override
    public Inventory hasSameInventoryQc(String lot, String goodsCode, String location, String shipmentCode, String goodsStatus,
                                      Integer isLock){
        // 批次、库位、商品、入驻商（ 商品、货主绑定在一起，似乎可以省略）、库存状态、是否锁定、容器（待扩展）,
        LambdaQueryWrapper<Inventory> queryWrapper = new QueryWrapper<Inventory>().lambda();
        queryWrapper.eq(Inventory::getLot, lot)
                .eq(Inventory::getLocation, location)
                .eq(Inventory::getGoodsCode, goodsCode)
                .eq(Inventory::getShipmentCode, shipmentCode)
                .eq(Inventory::getGoodsStatus, goodsStatus)
                //.eq(Inventory::getGmtStock, gmtStock)
                .eq(Inventory::getIsLock, isLock)
                .eq(Inventory::getTempInventory, NumConst.NUM_ZERO)
                .gt(Inventory::getQuantity, BigDecimal.ZERO)
                .eq(Inventory::getJobStatus, InventoryJobStatusEnum.NORMAL.getValue());

        Inventory sameInventory = this.getOne(queryWrapper);
        return sameInventory;
    }

    /**
     * 判断临时收货库位是否有相同的临时库存
     *
     * @param register
     * @param receive
     * @return
     */
    @Override
    public Inventory hasSameReceiveTempInventory(ReceiveRegister register, Receive receive) {
        String groupCode = register.getGroupCode();
        // 获取临时库位信息
        Location tempLocation = locationMapper.selectOne(new QueryWrapper<Location>().lambda()
                .eq(Location::getGroupCode,groupCode)
                .eq(Location::getWarehouseCode, receive.getWarehouseCode())
                .eq(Location::getSerialNumber, TempLocationTypeEnum.SH));
        if(null==tempLocation){
            log.error("仓库【{}】数据有问题，缺少收货临时库位",receive.getWarehouseCode());
            throw new ServiceException("仓库数据有问题，缺少收货临时库位");
        }

        // 判断临时库位上是否有相同的库存信息，根据收货单、收货单明细、入驻商、仓库、临时库位码、商品生产日期连带的10个字段、商品状态、容器
        Inventory inventory = this.getOne(new QueryWrapper<Inventory>().lambda()
                .eq(Inventory::getTempCode,register.getReceiveCode())
                .eq(Inventory::getTempItemCode,register.getReceiveItemCode())
                .eq(Inventory::getTempRegisterCode, register.getCode())
                .eq(Inventory::getWarehouseCode, receive.getWarehouseCode())
                .eq(Inventory::getLocation, tempLocation.getCode())
                .eq(Inventory::getShipmentCode, receive.getShipmentCode())
                .isNull(ObjectUtil.isNull(register.getGmtManufacture()), Inventory::getGmtManufacture)
                .eq(ObjectUtil.isNotNull(register.getGmtManufacture()), Inventory::getGmtManufacture, register.getGmtManufacture())
                .isNull(ObjectUtil.isNull(register.getGmtExpire()), Inventory::getGmtExpire)
                .eq(ObjectUtil.isNotNull(register.getGmtExpire()), Inventory::getGmtExpire, register.getGmtExpire())
                /*.isNull(ObjectUtil.isNull(register.getGmtStock()), Inventory::getGmtStock)
                .eq(ObjectUtil.isNotNull(register.getGmtStock()), Inventory::getGmtStock, register.getGmtStock())*/
                .isNull(ObjectUtil.isNull(register.getSupplier()), Inventory::getSupplier)
                .eq(ObjectUtil.isNotNull(register.getSupplier()), Inventory::getSupplier, register.getSupplier())
                .isNull(ObjectUtil.isNull(register.getSerialNumber()), Inventory::getSerialNumber)
                .eq(ObjectUtil.isNotNull(register.getSerialNumber()), Inventory::getSerialNumber, register.getSerialNumber())
                .isNull(ObjectUtil.isNull(register.getExtendOne()), Inventory::getExtendOne)
                .eq(ObjectUtil.isNotNull(register.getExtendOne()), Inventory::getExtendOne, register.getExtendOne())
                .isNull(ObjectUtil.isNull(register.getExtendTwo()), Inventory::getExtendTwo)
                .eq(ObjectUtil.isNotNull(register.getExtendTwo()), Inventory::getExtendTwo, register.getExtendTwo())
                .isNull(ObjectUtil.isNull(register.getExtendThree()), Inventory::getExtendThree)
                .eq(ObjectUtil.isNotNull(register.getExtendThree()), Inventory::getExtendThree, register.getExtendThree())
                .isNull(ObjectUtil.isNull(register.getExtendFour()), Inventory::getExtendFour)
                .eq(ObjectUtil.isNotNull(register.getExtendFour()), Inventory::getExtendFour, register.getExtendFour())
                .isNull(ObjectUtil.isNull(register.getExtendFive()), Inventory::getExtendFive)
                .eq(ObjectUtil.isNotNull(register.getExtendFive()), Inventory::getExtendFive, register.getExtendFive())
                .isNull(ObjectUtil.isNull(register.getExtendSix()), Inventory::getExtendSix)
                .eq(ObjectUtil.isNotNull(register.getExtendSix()), Inventory::getExtendSix, register.getExtendSix())
                .eq(Inventory::getContainerBarCode, ObjectUtil.defaultIfNull(register.getContainerBarCode(), StrUtil.EMPTY))
                .eq(Inventory::getGoodsStatus, register.getGoodsStatus())
                .eq(Inventory::getGroupCode,groupCode)
        );
        return inventory;
    }

    /**
     * 创建收货临时库位库存
     * @param register
     * @param receive
     * @param receiveItem
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Inventory saveReceiveTempInventory(ReceiveRegister register, Receive receive, ReceiveItem receiveItem) {
        String groupCode = register.getGroupCode();
        WarehouseSpace tempSpace = warehouseSpaceCache.getTempSpace(receive.getWarehouseCode(),groupCode);
        // 获取临时库位信息
        Location tempLocation = locationMapper.selectOne(new QueryWrapper<Location>().lambda()
                .eq(Location::getGroupCode,groupCode)
                .eq(Location::getWarehouseCode, receive.getWarehouseCode())
                .eq(Location::getSerialNumber, TempLocationTypeEnum.SH));
        // 生成收货临时库存
        Inventory inventory = new Inventory();
        inventory.setCode(IdUtil.simpleUUID());
        inventory.setGroupCode(groupCode);
        inventory.setTempCode(register.getReceiveCode());
        inventory.setTempItemCode(register.getReceiveItemCode());
        inventory.setTempRegisterCode(register.getCode());
        // 临时库存标注
        inventory.setTempInventory(1);

        inventory.setWarehouseCode(receive.getWarehouseCode());
        inventory.setWarehouseSpaceCode(tempSpace.getCode());
        inventory.setShipmentCode(receive.getShipmentCode());
        inventory.setShipmentName(receive.getShipmentName());
        inventory.setGoodsCode(receiveItem.getGoodsCode());
        inventory.setGoodCode(receiveItem.getGoodCode());
        inventory.setGoodsName(receiveItem.getGoodsName());
        inventory.setGoodsBarCode(receiveItem.getGoodsBarCode());
        inventory.setSpec(receiveItem.getSpec());
        //收货登记明细里的有效期为1时，有两种情况形成的，一是确实为1，二是为空时设置的一个默认值
        if (receiveItem.getExpirationQuantity().compareTo(BigDecimal.ONE) == NumConst.NUM_ZERO) {
            //找到对应的商品
            Goods goods = goodsCache.getObj(receiveItem.getGoodsCode());
            inventory.setExpirationQuantity(goods.getQuality());
        } else {
            inventory.setExpirationQuantity(receiveItem.getExpirationQuantity());
        }
        // 保存商品基本单位
        inventory.setGoodsUnit(receiveItem.getBasicUnit());
        inventory.setUnitName(unitCache.translate(inventory.getGoodsUnit()));
        // 库存数量 = 上架数量*最小单位转换件装量
        inventory.setQuantity(register.getReceivedQuantity().multiply(register.getUnitConvertQuantity()));
        inventory.setLocation(tempLocation.getCode());

        inventory.setIsLock(0);
        inventory.setStatus(register.getGoodsStatus());
        inventory.setGoodsStatus(register.getGoodsStatus());
        inventory.setGoodsStatusName(register.getGoodsStatusName());
        inventory.setContainerBarCode(register.getContainerBarCode());
        inventory.setDeleted(0);
        inventory.setGmtManufacture(register.getGmtManufacture());
        inventory.setGmtExpire(register.getGmtExpire());
        inventory.setGmtStock(LocalDateTime.now());
        inventory.setSupplier(register.getSupplier());
        inventory.setSerialNumber(register.getSerialNumber());
        inventory.setExtendOne(register.getExtendOne());
        inventory.setExtendTwo(register.getExtendTwo());
        inventory.setExtendThree(register.getExtendThree());
        inventory.setExtendFour(register.getExtendFour());
        inventory.setExtendFive(register.getExtendFive());
        inventory.setExtendSix(register.getExtendSix());

        this.save(inventory);

        return inventory;
    }

    /**
     * 创建出货临时库存
     *
     * @param distribution
     * @param delivery
     * @param deliveryItem
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Inventory saveDeliveryTempInventory(DeliveryDistribution distribution, Delivery delivery, DeliveryItem deliveryItem, Inventory oldInventory) {
        WarehouseSpace tempSpace = warehouseSpaceCache.getTempSpace(delivery.getWarehouseCode(), UserUtil.getBranchCode());

        //暂时这里针对商品的保质期天数做修改（后期得去改商品的保质期，现在没改是怕对别的有影响）
        if (null != oldInventory.getExpirationQuantity() && oldInventory.getExpirationQuantity().compareTo(BigDecimal.ZERO) == NumConst.NUM_ZERO) {
            //找到对应的商品
            Goods goods = goodsCache.getObj(oldInventory.getGoodsCode());
            oldInventory.setExpirationQuantity(goods.getQuality());
        } else {
            oldInventory.setExpirationQuantity(oldInventory.getExpirationQuantity());
        }

        // 获取临时库位信息
        Location tempLocation = locationMapper.selectOne(new QueryWrapper<Location>().lambda()
                .eq(Location::getGroupCode,UserUtil.getBranchCode())
                .eq(Location::getWarehouseCode, delivery.getWarehouseCode())
                .eq(Location::getSerialNumber, TempLocationTypeEnum.FH));
        // 生成收货临时库存
        Inventory inventory = new Inventory();
        BeanUtil.copyProperties(oldInventory, inventory);
        inventory.setCode(IdUtil.simpleUUID());
        inventory.setGroupCode(UserUtil.getBranchCode());
        inventory.setDeleted(0);
        inventory.setCreatedBy(UserUtil.getCode());
        inventory.setCreatedName(partyCache.translate(UserUtil.getCode()));
        inventory.setModifiedName("");
        inventory.setModifiedBy("");
        inventory.setTempCode(distribution.getDeliveryCode());
        inventory.setTempItemCode(distribution.getDeliveryItemCode());
        inventory.setTempRegisterCode(distribution.getCode());
        inventory.setOldInventoryCode(oldInventory.getCode());
        inventory.setOldLocation(oldInventory.getLocation());
        // 临时库存标注
        inventory.setTempInventory(1);
        // 库存数量
        inventory.setQuantity(distribution.getDistributionQuantity());
        // 被使用数量为0
        inventory.setUseQuantity(BigDecimal.ZERO);
        inventory.setLocation(tempLocation.getCode());
        inventory.setWarehouseSpaceCode(tempSpace.getCode());
        this.save(inventory);

        return inventory;
    }

    /**
     * 判断是否有相同的发货临时库存
     *
     * @param distribution
     * @param delivery
     * @return
     */
    @Override
    public Inventory hasSameDeliveryTempInventory(DeliveryDistribution distribution, Delivery delivery) {
        // 判断临时库位上是否有相同的库存信息，直接根据临时库存关联的确认单号判断
        Inventory inventory = this.getOne(new QueryWrapper<Inventory>().lambda()
                .eq(Inventory::getTempRegisterCode, distribution.getCode()).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
        return inventory;
    }

    /**
     * 取消分配、缺货时返还库存
     *
     * @param tempInventory
     * @param returnQuantity
     * @return
     */
    @Override
    public Result returnInventory(Inventory tempInventory, BigDecimal returnQuantity) {
        // 暂不考虑原库存是否被锁，直接原路返还，因为存在手动将锁住的库存分配的逻辑，所以无法判断返还的库存是否应该被锁
        adjustQuantity(tempInventory.getOldInventoryCode(), returnQuantity);

        // 临时库位的数量需要减掉
        adjustQuantity(tempInventory.getCode(), returnQuantity.negate());

        return Result.ok();
    }

    /**
     * @param inventoryCode    : 操作的临时库存
     * @param stockoutQuantity : 缺货数量（返还数量）
     * @param type             :  操作类型
     * @param sourceCode       :  操作单据号
     * @description 缺货登记时返还库存
     * @author xuyang
     * @date 2020/10/23 10:02
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result stockoutInventory(String inventoryCode, BigDecimal stockoutQuantity, String type, String sourceCode) {
        Inventory inv = getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, inventoryCode).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
        Inventory sameInv;
        // 复制一份新的库存，然后抹去现库存的信息记录，作为新库存的信息保存
        Inventory newInv = new Inventory();
        BeanUtils.copyProperties(inv, newInv);
        newInv.setCode(null);
        newInv.setCreatedBy(null);
        newInv.setQuantity(stockoutQuantity);
        newInv.setUseQuantity(BigDecimal.ZERO);
        newInv.setTempCode(StrUtil.EMPTY);
        newInv.setTempItemCode(StrUtil.EMPTY);
        newInv.setTempRegisterCode(StrUtil.EMPTY);

        switch (StockoutTypeEnum.getByValue(type)){
            case PICKING:
                // 拣货时缺货登记，将库存返还原库位，同时将库存锁定
                // 现库存减少
                //this.adjustQuantity(inventoryCode, stockoutQuantity.negate());
                this.reduceQuantity(inventoryCode,stockoutQuantity);
                // 获取原库存库位
                Location oldLocation = locationCache.getObj(inv.getOldLocation());
                if (ObjectUtil.isNull(oldLocation)) {
                    throw new ServiceException("退件的原库位不存在");
                }
                // 返还的库存是锁定状态的，临时库存标志改为非临时库存的, 库位、库区改为原库位的
                newInv.setIsLock(1);
                newInv.setTempInventory(0);
                newInv.setLocation(oldLocation.getCode());
                newInv.setWarehouseSpaceCode(oldLocation.getSpaceCode());
                // 由于库存状态改为锁定，返还时不一定回到原库存上，需要重新判断，如果一致，则可在原库存上调整
                //sameInv = this.hasSameInventory(newInv.getLot(), newInv.getGoodsCode(), newInv.getOldLocation(), newInv.getShipmentCode(),
                //        newInv.getGoodsStatus(),newInv.getIsLock(), newInv.getContainerBarCode(), newInv.getGmtStock());
                sameInv = this.getOne(new LambdaQueryWrapper<Inventory>()
                        .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                        .eq(Inventory::getOldInventoryCode,inv.getOldInventoryCode())
                        .eq(Inventory::getIsLock,1)
                        .eq(Inventory::getTempInventory,0).last("limit 1")
                );

                if(sameInv == null){
                    save(newInv);
                }else{
                    // 在原库存基础上增加数量
                    //adjustQuantity(sameInv.getCode(), stockoutQuantity);
                    this.addQuantity(sameInv.getCode(),stockoutQuantity);
                }
                // 释放临时库存的作业状态
                this.releaseEmptyTempInvJobStatus(inventoryCode);
                // 记录库存变动日志, 需要两条，临时库位减少， 原库位（或新库位）增加
                iInventoryRecordService.insertRecord(inv, sourceCode, stockoutQuantity.negate(), InventoryRecordTypeEnum.PICK.getValue());
                iInventoryRecordService.insertRecord(newInv, sourceCode, stockoutQuantity, InventoryRecordTypeEnum.PICK.getValue());
                break;
            case OUT_CHECK:
                // 出库复核时退减， 出库临时库存减少，退减库存增加

                // 出库临时库位库存减少
                //this.adjustQuantity(inventoryCode, stockoutQuantity.negate());
                this.reduceQuantity(inventoryCode,stockoutQuantity);
                // 抹去容器号等信息
                newInv.setContainerBarCode(StrUtil.EMPTY);

                // 获取退减临时库位
                Location reLocation = locationMapper.selectOne(new QueryWrapper<Location>().lambda()
                        .eq(Location::getGroupCode,UserUtil.getBranchCode())
                        .eq(Location::getWarehouseCode, newInv.getWarehouseCode())
                        .eq(Location::getSerialNumber, TempLocationTypeEnum.RE));
                newInv.setLocation(reLocation.getCode());

                // 判断退减库位上是否有相同信息
                sameInv = this.hasSameInventory(newInv.getLot(), newInv.getGoodsCode(), reLocation.getCode(), newInv.getShipmentCode(),
                        newInv.getGoodsStatus(), newInv.getIsLock(), newInv.getContainerBarCode(), newInv.getGmtStock());
                // 合并或保存, 有相同数据则在原库存基础上增加数量，没有则新增
                if(sameInv == null){
                    save(newInv);
                }else{
                    //adjustQuantity(sameInv.getCode(), stockoutQuantity);
                    this.addQuantity(sameInv.getCode(),stockoutQuantity);
                }
                // 记录库存变动日志，两条，发货临时库存减少，退减临时库存增加
                iInventoryRecordService.insertRecord(inv, sourceCode, stockoutQuantity.negate(), InventoryRecordTypeEnum.OUTBOUND.getValue());
                iInventoryRecordService.insertRecord(newInv, sourceCode, stockoutQuantity, InventoryRecordTypeEnum.OUTBOUND.getValue());
                break;
            default:
        }


        return Result.ok();
    }

    /**
     * 库存存储托盘数量
     *
     * @param code 库位code
     * @return
     */
    @Override
    public Integer countPallet(String code) {
        return this.baseMapper.countPallet(code);
    }


    /**
     * 更新库存数量，正数为增加库存，负数为减少库存
     * @param inventoryCode
     * @param adjustQuantity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean adjustQuantity(String inventoryCode, BigDecimal adjustQuantity){
        boolean actionFlag = this.update(new UpdateWrapper<Inventory>().lambda()
                .eq(Inventory::getCode, inventoryCode)
                .setSql(" quantity = quantity + " + adjustQuantity)
                // 调整后的数量要保证不能少于占用数量
//                .apply(" quantity + " + adjustQuantity + " >= use_quantity " )
        );
        if (!actionFlag) {
            throw new ServiceException("库存调整数量超过了可操作数量");
        }

        return actionFlag;
    }



    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean reduceQuantity(String inventoryCode, BigDecimal adjustQuantity){
      return baseMapper.subtractInventory(inventoryCode,adjustQuantity)>0;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean addQuantity(String inventoryCode, BigDecimal adjustQuantity){
      return baseMapper.addInventory(inventoryCode,adjustQuantity)>0;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean adjustUsedQuantity(String inventoryCode, BigDecimal adjustQuantity) {
        return this.update(new UpdateWrapper<Inventory>().lambda()
                .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .eq(Inventory::getCode, inventoryCode)
                .setSql(" use_quantity = use_quantity + " + adjustQuantity)
        );
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean adjustQuantityAndUseQuantity(String inventoryCode, BigDecimal adjustQuantity, BigDecimal adjustUseQuantity) {
        return this.update(new UpdateWrapper<Inventory>().lambda()
                .eq(Inventory::getCode, inventoryCode)
                .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .setSql(" quantity = quantity + " + adjustQuantity)
                .setSql("use_quantity = use_quantity + " + adjustQuantity)
        );
    }

    /**
     * @param containerBarCode :
     * @description 判断库存中是否有使用该容器号的库存
     * @author xuyang
     * @date 2020/10/29 14:23
     **/
    @Override
    public boolean noUseContainerBarCode(String containerBarCode) {
        boolean flag = false;
        // 查询库存，容器号相同、库存数量>0的， 由于可能存在同一个库存，同时进行收货、发货操作，所以不判断是否是临时库存
        List<Inventory> inventories = this.list(new QueryWrapper<Inventory>().lambda().eq(Inventory::getContainerBarCode, containerBarCode)
                .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .gt(Inventory::getQuantity, BigDecimal.ZERO));
        if(inventories.isEmpty()){
            flag = true;
        }
        return flag;
    }

    /**
     * @param tempInvCode :
     * @description 将库存作业状态设为工作中
     * @author xuyang
     * @date 2020/10/31 9:31
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result setTempInvJobStatusWorking(String tempInvCode) {
        this.update(new UpdateWrapper<Inventory>().lambda().eq(Inventory::getCode, tempInvCode)
                .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .set(Inventory::getJobStatus, InventoryJobStatusEnum.WORKING.getValue()));
        return Result.ok();
    }

    /**
     * @param tempInvCode :
     * @description 将库存（临时库存）作业状态改为正常，条件是，临时库存全部被释放了
     * @author xuyang
     * @date 2020/10/31 9:31
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result releaseEmptyTempInvJobStatus(String tempInvCode) {
        Inventory tempInv = this.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, tempInvCode).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
        if(tempInv.getQuantity().compareTo(BigDecimal.ZERO) == 0){
            this.update(new UpdateWrapper<Inventory>().lambda().eq(Inventory::getCode, tempInvCode)
                    .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                    .set(Inventory::getJobStatus, InventoryJobStatusEnum.NORMAL.getValue()));
        }
        return Result.ok();
    }

    /**
     * @param tempInvCode :
     * @description 将库存（临时库存）作业状态改为正常，用于拣货确认后的操作
     * @author xuyang
     * @date 2020/10/31 9:31
     **/
    @Override
    public Result releaseTempInvJobStatus(String tempInvCode) {
        this.update(new UpdateWrapper<Inventory>().lambda().eq(Inventory::getCode, tempInvCode)
                .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .set(Inventory::getJobStatus, InventoryJobStatusEnum.NORMAL.getValue()));
        return Result.ok();
    }

    /**
     * 判断仓库下是否有库存，无论是否是临时库存
     *
     * @param warehouseCode
     * @return
     */
    @Override
    public boolean warehouseHasInv(String warehouseCode) {
        boolean flag = false;
        InventoryDTO inventoryDTO = new InventoryDTO();
        inventoryDTO.setWarehouseCode(warehouseCode);
        // 2：跳过临时库存标志位查询
        inventoryDTO.setTempInventory(2);

        BigDecimal sum = queryInventorySum(inventoryDTO);
        if(sum.compareTo(BigDecimal.ZERO) != 0){
            flag = true;
        }

        return flag;
    }

    /**
     * 根据条件查询库存总量
     *
     * @param inventoryDTO
     * @return
     */
    @Override
    public BigDecimal queryInventorySum(InventoryDTO inventoryDTO) {
        return this.baseMapper.queryInventorySum(inventoryDTO);
    }

    /**
     * 获取仓库库存
     *
     * @param warehouseCodes 仓库code
     * @return Map.key="quatity","warehouseCode"
     */
    @Override
    public List<Map<String, Object>> getQuantity(List<String> warehouseCodes) {
        return this.baseMapper.getQuantity(warehouseCodes,UserUtil.getBranchCode());
    }

    /**
     * 锁可用库存
     * 库存表中占用数量增加，如果最终的可用数量<0的话，则报错，回滚
     * @param warehouseCode
     * @param goodsCode
     * @param goodsName
     * @param shipmentCode
     * @param lockQuantity
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void lockUsefulInventory(String warehouseCode, String goodsCode, String goodsName, String shipmentCode, BigDecimal lockQuantity) {
        // 增加占用数量， 数量条件是 总数量 >= 原占用数量 + 不可用数量 + 新占用数量
        boolean updateFlag = this.update(new UpdateWrapper<Inventory>().lambda().eq(Inventory::getWarehouseCode, warehouseCode)
                .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .eq(Inventory::getGoodsCode, goodsCode).eq(Inventory::getShipmentCode, shipmentCode)
                .apply(" quantity >= use_quantity + " + lockQuantity)
                .setSql(" use_quantity = use_quantity + " + lockQuantity));
        if(!updateFlag){
            throw new ServiceException("商品“" + goodsName + "”可用库存不足。");
        }
    }

    /**
     * 计算不可用库存数量；不可用库存是指库存状态为不可发货的库存；
     *
     * @param inventoryDTO
     * @return
     */
    @Override
    public BigDecimal countUnusableInv(InventoryDTO inventoryDTO) {
        return this.baseMapper.countUnusableInv(inventoryDTO);
    }

    /**
     * 更新oms不可用库存数量，进一步封装countUnusableInv方法
     *
     * @param inventoryDTO
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateOmsContUseQuantity(InventoryDTO inventoryDTO) {
        // 如果仓库-入驻商 使用了oms，则更新oms不可用库存
        WarehouseShipment warehouseShipment = warehouseShipmentCache.getShipment(inventoryDTO.getWarehouseCode(), inventoryDTO.getShipmentCode());
        if (warehouseShipment == null ) {
            return;
        }
        if (NumConst.NUM_ONE.equals(warehouseShipment.getUseOms())) {
            BigDecimal contUseQuantity = countUnusableInv(inventoryDTO);
            iOmsInventoryService.refreshOmsContUseQuantity(inventoryDTO.getWarehouseCode(), inventoryDTO.getShipmentCode(), inventoryDTO.getGoodsCode(), contUseQuantity);
        }

    }

    /**
     * 更新oms不可用库存数量，进一步封装countUnusableInv方法
     *
     * @param warehouseCode
     * @param shipmentCode
     * @param goodsCode
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateOmsContUseQuantity(String warehouseCode, String shipmentCode, String goodsCode) {
        InventoryDTO inventoryDTO = new InventoryDTO();
        inventoryDTO.setWarehouseCode(warehouseCode);
        inventoryDTO.setShipmentCode(shipmentCode);
        inventoryDTO.setGoodsCode(goodsCode);
        updateOmsContUseQuantity(inventoryDTO);
    }

    /**
     * 根据货主、库存状态值获取不可用库存信息，包括仓库、商品、不可用数量；用于更新oms不可用库存数量
     *
     * @param inventoryDTO
     * @return
     */
    @Override
    public List<InventoryVO> getContUseQuantityInfo(InventoryDTO inventoryDTO) {
        return this.baseMapper.getContUseQuantityInfo(inventoryDTO);
    }

    /**
     * 执行原生sql
     *
     * @param sql
     */
    @Override
    public void execSql(String sql) {
        this.baseMapper.execSql(sql);
    }

    /**
     * 查询批次规则，将未勾选的值去除，避免更新之前商品批次规则发生变化
     * @param inv
     */
    private void adjustInventoryValueByBatchRule(Inventory inv) {
        Goods goods = goodsCache.getObj(inv.getGoodsCode());
        if (goods == null) {
            // TODO 查询不到商品，暂不做处理
            return;
        }
        BatchRule batchRule = batchRuleCache.getObj(goods.getLotNumber());
        if (batchRule == null) {
            // TODO 查询不到批次规则，暂不做处理
            return;
        }
        // 根据规则，将未勾选的字段清空, 存货日期除外，该字段必须存在
        if (NumConst.NUM_ZERO.equals(batchRule.getLot())) {
            inv.setReceiveLot(StrUtil.EMPTY);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getGmtManufacture())) {
            inv.setGmtManufacture(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getGmtExpire())) {
            inv.setGmtExpire(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getSupplier())) {
            inv.setSupplier(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getSerialNumber())) {
            inv.setSerialNumber(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getExtendOne())) {
            inv.setExtendOne(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getExtendTwo())) {
            inv.setExtendTwo(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getExtendThree())) {
            inv.setExtendThree(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getExtendFour())) {
            inv.setExtendFour(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getExtendFive())) {
            inv.setExtendFive(null);
        }
        if (NumConst.NUM_ZERO.equals(batchRule.getExtendSix())) {
            inv.setExtendSix(null);
        }




    }

    /**
     * 获取推荐库位 (商品占用数量最少的库位)
     * @param inventoryDTO
     */
    @Override
    public List<InventoryVO> getRecommondLocation(InventoryDTO inventoryDTO){
        return  this.baseMapper.getRecommondLocations(inventoryDTO);
    }





    /**
     * 判断是否有相同的预打包临时库存
     * @param distribution
     * @return
     */
    @Override
    public Inventory hasSamePrepackagedTempInventory(PrepackagedDistribution distribution){
        // 判断临时库位上是否有相同的库存信息，直接根据临时库存关联的确认单号判断
        Inventory inventory = this.getOne(new QueryWrapper<Inventory>().lambda()
                .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                .eq(Inventory::getTempRegisterCode, distribution.getCode()));
        return inventory;
    }

    /**
     * 创建预打包临时库存
     * @param distribution
     * @param prepackaged
     * @param oldInventory
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Inventory savePrepackagedTempInventory(PrepackagedDistribution distribution, Prepackaged prepackaged,Inventory oldInventory){
        WarehouseSpace tempSpace = warehouseSpaceCache.getTempSpace(prepackaged.getWarehouseCode(),UserUtil.getBranchCode());

        // 获取临时库位信息
        Location tempLocation = locationMapper.selectOne(new QueryWrapper<Location>().lambda()
                .eq(Location::getGroupCode,UserUtil.getBranchCode())
                .eq(Location::getWarehouseCode, prepackaged.getWarehouseCode())
                .eq(Location::getSerialNumber, TempLocationTypeEnum.YD));
        // 生成收货临时库存
        Inventory inventory = new Inventory();
        BeanUtil.copyProperties(oldInventory, inventory);
        inventory.setCode(IdUtil.simpleUUID());
        inventory.setGroupCode(UserUtil.getBranchCode());
        inventory.setDeleted(0);
        inventory.setCreatedBy(UserUtil.getCode());
        inventory.setCreatedName(partyCache.translate(UserUtil.getCode()));
        inventory.setModifiedName("");
        inventory.setModifiedBy("");
        inventory.setTempCode(distribution.getPrepackagedCode());
        inventory.setTempItemCode(distribution.getPrepackagedItemCode());
        inventory.setTempRegisterCode(distribution.getCode());
        inventory.setOldInventoryCode(oldInventory.getCode());
        inventory.setOldLocation(oldInventory.getLocation());
        // 临时库存标注
        inventory.setTempInventory(1);
        // 库存数量
        inventory.setQuantity(distribution.getDistributionQuantity());
        // 被使用数量为0
        inventory.setUseQuantity(BigDecimal.ZERO);
        inventory.setLocation(tempLocation.getCode());
        inventory.setWarehouseSpaceCode(tempSpace.getCode());

        this.save(inventory);

        return inventory;
    };






    /**
     * 创建预打包临时库存
     * @param receiveRegisterList
     * @param receiveItems
     * @param receive
     * @return
     */
    @Override
    public List<Inventory> batchGenReceiveTempInventory(List<ReceiveRegister> receiveRegisterList,List<ReceiveItem> receiveItems,Receive receive){
        List<Inventory> inventories = Lists.newArrayList();
        String warehouseCode = receive.getWarehouseCode();
        String shipmentCode = receive.getShipmentCode();
        String shipmentName = receive.getShipmentName();
        Map<String,ReceiveItem> itemMap = receiveItems.stream().collect(Collectors.toMap(ReceiveItem::getCode, e -> e));
        receiveRegisterList.stream().forEach(register -> {
            ReceiveItem receiveItem = MapUtil.get(itemMap, register.getReceiveItemCode(), ReceiveItem.class);
            String groupCode = register.getGroupCode();
            WarehouseSpace tempSpace = warehouseSpaceCache.getTempSpace(receive.getWarehouseCode(),groupCode);
            // 获取临时库位信息
            Location tempLocation = locationMapper.selectOne(new QueryWrapper<Location>().lambda()
                    .eq(Location::getGroupCode,groupCode)
                    .eq(Location::getWarehouseCode, receive.getWarehouseCode())
                    .eq(Location::getSerialNumber, TempLocationTypeEnum.SH));
            // 生成收货临时库存
            Inventory inventory = new Inventory();
            inventory.setCode(IdUtil.simpleUUID());
            inventory.setGroupCode(groupCode);
            inventory.setTempCode(register.getReceiveCode());
            inventory.setTempItemCode(register.getReceiveItemCode());
            inventory.setTempRegisterCode(register.getCode());
            // 临时库存标注
            inventory.setTempInventory(1);

            inventory.setWarehouseCode(warehouseCode);
            inventory.setWarehouseSpaceCode(tempSpace.getCode());
            inventory.setShipmentCode(shipmentCode);
            inventory.setShipmentName(shipmentName);
            inventory.setGoodsCode(receiveItem.getGoodsCode());
            inventory.setGoodCode(receiveItem.getGoodCode());
            inventory.setGoodsName(receiveItem.getGoodsName());
            inventory.setGoodsBarCode(receiveItem.getGoodsBarCode());
            inventory.setSpec(receiveItem.getSpec());
            //收货登记明细里的有效期为1时，有两种情况形成的，一是确实为1，二是为空时设置的一个默认值
            if (receiveItem.getExpirationQuantity().compareTo(BigDecimal.ONE) == NumConst.NUM_ZERO) {
                //找到对应的商品
                Goods goods = goodsCache.getObj(receiveItem.getGoodsCode());
                inventory.setExpirationQuantity(goods.getQuality());
            } else {
                inventory.setExpirationQuantity(receiveItem.getExpirationQuantity());
            }
            // 保存商品基本单位
            inventory.setGoodsUnit(receiveItem.getBasicUnit());
            inventory.setUnitName(unitCache.translate(inventory.getGoodsUnit()));
            // 库存数量 = 上架数量*最小单位转换件装量
            inventory.setQuantity(register.getReceivedQuantity().multiply(register.getUnitConvertQuantity()));
            inventory.setLocation(tempLocation.getCode());

            inventory.setIsLock(0);
            inventory.setStatus(register.getGoodsStatus());
            inventory.setGoodsStatus(register.getGoodsStatus());
            inventory.setGoodsStatusName(register.getGoodsStatusName());
            inventory.setDeleted(0);
            inventory.setGmtManufacture(register.getGmtManufacture());
            inventory.setGmtExpire(register.getGmtExpire());
            inventory.setGmtStock(LocalDateTime.now());
            inventory.setSupplier(register.getSupplier());
            inventory.setSerialNumber(register.getSerialNumber());
            inventory.setExtendOne(register.getExtendOne());
            inventory.setExtendTwo(register.getExtendTwo());
            inventory.setExtendThree(register.getExtendThree());
            inventory.setExtendFour(register.getExtendFour());
            inventory.setExtendFive(register.getExtendFive());
            inventory.setExtendSix(register.getExtendSix());
            inventories.add(inventory);
        });
        return inventories;

    }


}
